package com.ptkc.core.esearch.repos;

import com.alibaba.fastjson.JSON;
import com.ptkc.core.esearch.model.*;
import com.ptkc.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.aggregations.metrics.max.Max;
import org.elasticsearch.search.aggregations.metrics.max.MaxAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.sum.Sum;
import org.elasticsearch.search.aggregations.metrics.sum.SumAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.joda.time.DateTimeZone;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author: Mr.Jiao
 * @date: 2018年11月28日
 */
@Component
@Slf4j
public class BusinessRepository {

    private RestHighLevelClient restHighLevelClient;

    private final String INDEX = "runfast_business";

    private final String TYPE = "business";

//    @Autowired
//    private ActivitiesRepository activitiesRepository;
//
//    @Autowired
//    private DeliverCostRepository deliverCostRepository;

    private final DelayQueueRunner<Order> concurrentHashMap = new DelayQueueRunner<Order>() {
        @Override
        public void run(String s, Order order) {
            if(order != null){
                try {
                    BusinessRepository.this.recordSalesVolume(order);
                } catch (Exception e) {
                    log.error(String.format("统计商家销量信息失败,busID:", order.getBusinessId()));
                }
            }
        }
    };

//    private final DelayQueueRunner<Integer> queueRunnerDeliverCost = new DelayQueueRunner<Integer>() {
//        @Override
//        public void run(String k, Integer integer) {
//            if(integer != null) {
//                try {
//                    BusinessRepository.this.setDefaultDeliverCostRunner(integer);
//                } catch (Exception e) {
//                    e.printStackTrace();
//                    log.error(e.getMessage());
//                }
//            }
//        }
//    };
//
//    private final DelayQueueRunner<Integer> queueRunnerActivities = new DelayQueueRunner<Integer>() {
//        @Override
//        public void run(String k, Integer integer) {
//            try {
//                BusinessRepository.this.initActivities(integer);
//            } catch (Exception e) {
//                e.printStackTrace();
//                log.error(e.getMessage());
//            }
//        }
//    };
//
//    private final SingleThreadRunner<Business> queueRunnerBusDefaultDeliverCost = new SingleThreadRunner<Business>() {
//        @Override
//        public void run(String k, Business bus) {
//            try {
//                BusinessRepository.this.setSelfDeliver(bus.getId(), bus.getAgentId());
//            } catch (Exception e) {
//                e.printStackTrace();
//                log.error(e.getMessage());
//            }
//        }
//    };

    private ESUtil esUtil = null;

    public BusinessRepository(RestHighLevelClient restHighLevelClient){
        concurrentHashMap.start();
//        queueRunnerDeliverCost.start();
//        queueRunnerActivities.start();
//        queueRunnerBusDefaultDeliverCost.start();
        this.restHighLevelClient = restHighLevelClient;
        this.esUtil = new ESUtil(restHighLevelClient);
    }


    public boolean save(Business business) throws IOException {
        if(business != null && business.getId() != null) {
            //自配送
            if(business.getIsDeliver() != null && business.getIsDeliver().equals(1)){
                if(NumberUtils.isDecimal(business.getLongitude()) && NumberUtils.isDecimal(business.getLatitude()) && business.getMaxDistance() != null) {
                    //自配送以商家坐标为中心点画圆
                    Map<String, Object> geoShareMap = new HashMap<>();
                    geoShareMap.put("type", "circle");
                    geoShareMap.put("coordinates", new Object[]{Double.valueOf(business.getLongitude()), Double.valueOf(business.getLatitude())});
                    geoShareMap.put("radius", String.format("%skm",business.getMaxDistance()));
                    business.setSelfDistRangeShape(geoShareMap);
                }
            }else {
                //平台配送
                if(StringUtil.isNotEmpty(business.getDistRange())) {
                    String[] distRanges = business.getDistRange().split("\\|");
                    List<Point> points = new ArrayList<>();
                    for (String point : distRanges) {
                        if (StringUtil.isNotEmpty(point) && point.indexOf(",") > 0) {
                            String[] pointArray = point.split(",");
                            if (pointArray.length == 2) {
                                if (NumberUtils.isDecimal(pointArray[0]) && NumberUtils.isDecimal(pointArray[1])) {
                                    Point p = new Point();
                                    p.setLng(Double.valueOf(pointArray[0]));
                                    p.setLat(Double.valueOf(pointArray[1]));
                                    points.add(p);
                                }
                            }
                        }
                    }
                    if (points.size() >= 3) {
                        //平台配送 商家范围 闭合的多边形
                        Map<String, Object> geoShareMap = new HashMap<>();
                        geoShareMap.put("type", "polygon");
                        Object[] coordinates = new Object[points.size() + 1];
                        for (int i = 0; i < points.size(); i++) {
                            Point point = points.get(i);
                            coordinates[i] = new double[]{point.getLng(), point.getLat()};
                        }
                        coordinates[coordinates.length - 1] = coordinates[0];
                        geoShareMap.put("coordinates", new Object[]{coordinates});
                        business.setDistRangeShape(geoShareMap);
                    }
                }
            }
            if(NumberUtils.isDecimal(business.getLongitude()) && NumberUtils.isDecimal(business.getLatitude())){
                GeoPoint point = new GeoPoint(Double.valueOf(business.getLatitude()), Double.valueOf(business.getLongitude()));
                business.setBusPoint(point);
            }
            DocWriteResponse response = this.esUtil.save(INDEX, TYPE, String.valueOf(business.getId()), business);
            if (response.status() == RestStatus.OK || response.status() == RestStatus.CREATED) {
//                if(business.getIsDeliver() == null || !business.getIsDeliver().equals(1)){
//                    queueRunnerBusDefaultDeliverCost.put(String.valueOf(business.getId()), business);
//                }
//                if(response.status() == RestStatus.CREATED){
//                    BusinessRepository.this.addToInitAcitivties(business.getId());
//                }
                return true;
            }
        }
        return false;
    }

    public boolean del(String id) throws IOException {
        DeleteRequest request = new DeleteRequest(INDEX, TYPE, id);
        DeleteResponse response = restHighLevelClient.delete(request);
        return response.status() == RestStatus.OK;
    }


    private Business getBus(String id) throws IOException {
        GetRequest request = new GetRequest(INDEX, TYPE, id);
        GetResponse response = restHighLevelClient.get(request);
        if(!response.isExists()){
            return null;
        }
        try{
            return JSON.parseObject(response.getSourceAsString(), Business.class);
        }catch (Exception e){
            return null;
        }
    }


    private void recordSalesVolume(Order order) throws IOException {
        SearchRequest searchRequest = new SearchRequest();
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder qbs = QueryBuilders.boolQuery();
        qbs.must(QueryBuilders.termQuery("status", 8));
        qbs.must(QueryBuilders.termQuery("isPay", 1));
        qbs.must(QueryBuilders.termQuery("businessId", order.getBusinessId()));
        Date endDate = DatesUtil.getDayEnd();
        Date startDate = DatesUtil.getBeginDayOfLastMonth();
        //订单时间(记录到小时)
        qbs.must(QueryBuilders.rangeQuery("createTime").from(startDate.getTime(), true).to(endDate.getTime(), true));
        sourceBuilder.query(qbs);
        DateHistogramAggregationBuilder timeBuilder = AggregationBuilders.dateHistogram("agg_create_time").field("createTime").dateHistogramInterval(DateHistogramInterval.days(1)).timeZone(DateTimeZone.forID("Asia/Shanghai"));
        //聚合查询
        SumAggregationBuilder agg_totalPay = AggregationBuilders.sum("agg_totalpay").field("totalpay");
        SumAggregationBuilder agg_totalRefund = AggregationBuilders.sum("agg_refund").script(new Script(ScriptType.STORED, null,"runfast_order_format_refund", new HashMap<>()));

        MaxAggregationBuilder maxAggregationBuilder = AggregationBuilders.max("agg_Maxtime").field("createTime");

        timeBuilder.subAggregation(agg_totalPay).subAggregation(agg_totalRefund).subAggregation(maxAggregationBuilder);
        sourceBuilder.aggregation(timeBuilder).from(0).size(0);
        searchRequest.indices("runfast_order").types("order").source(sourceBuilder);
        SearchResponse response = restHighLevelClient.search(searchRequest);
        if(response.status() == RestStatus.OK){
            Business business = new Business();
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
            Aggregations agg = response.getAggregations();
            Histogram byDay = agg.get("agg_create_time");
            Long maxOrderMillisecond = null;
            for(Histogram.Bucket bucket : byDay.getBuckets()){
                String timer = bucket.getKeyAsString();
                if(StringUtil.isEmpty(timer)){
                    continue;
                }
                ParsePosition pos = new ParsePosition(0);
                Date orderDate = dateFormat.parse(timer, pos);
                Sum sumTotalPay = bucket.getAggregations().get("agg_totalpay");
                Sum sumRefund = bucket.getAggregations().get("agg_refund");

                Max maxDate = bucket.getAggregations().get("agg_Maxtime");
                long _maxCMillisecond = (long)maxDate.getValue();
                if(maxOrderMillisecond == null){
                    maxOrderMillisecond = _maxCMillisecond;
                }else if(maxOrderMillisecond < _maxCMillisecond){
                    maxOrderMillisecond = _maxCMillisecond;
                }
                int orderQuantity = (int)bucket.getDocCount();
                BigDecimal totalPay = BigDecimal.valueOf(sumTotalPay.getValue());
                BigDecimal refund = BigDecimal.valueOf(sumRefund.getValue());
                BigDecimal actualPay = totalPay.subtract(refund);
                if(DatesUtil.isToday(orderDate)){
                    BigDecimal payAmount = business.getTodayPayAmount();
                    if(payAmount == null) {
                        business.setTodayPayAmount(actualPay);
                    }else{
                        business.setTodayPayAmount(payAmount.add(actualPay));
                    }
                    Integer quantity = business.getTodayOrderQuantity();
                    if(quantity == null){
                        business.setTodayOrderQuantity(orderQuantity);
                    }else{
                        business.setTodayOrderQuantity(orderQuantity + quantity);
                    }
                }else if(DatesUtil.isYesterday(orderDate)){
                    BigDecimal payAmount = business.getYesterPayAmount();
                    if(payAmount == null) {
                        business.setYesterPayAmount(actualPay);
                    }else{
                        business.setYesterPayAmount(payAmount.add(actualPay));
                    }
                    Integer quantity = business.getYesterOrderQuantity();
                    if(quantity == null){
                        business.setYesterOrderQuantity(orderQuantity);
                    }else{
                        business.setYesterOrderQuantity(orderQuantity + quantity);
                    }
                }

                if(DatesUtil.isThisweek(orderDate)){
                    BigDecimal payAmount = business.getThisWeekPayAmount();
                    if(payAmount == null) {
                        business.setThisWeekPayAmount(actualPay);
                    }else{
                        business.setThisWeekPayAmount(payAmount.add(actualPay));
                    }
                    Integer quantity = business.getThisWeekOrderQuantity();
                    if(quantity == null){
                        business.setThisWeekOrderQuantity(orderQuantity);
                    }else{
                        business.setThisWeekOrderQuantity(orderQuantity + quantity);
                    }
                }else if(DatesUtil.isLastweek(orderDate)){
                    BigDecimal payAmount = business.getLastWeekPayAmount();
                    if(payAmount == null) {
                        business.setLastWeekPayAmount(actualPay);
                    }else{
                        business.setLastWeekPayAmount(payAmount.add(actualPay));
                    }
                    Integer quantity = business.getLastWeekOrderQuantity();
                    if(quantity == null){
                        business.setLastWeekOrderQuantity(orderQuantity);
                    }else{
                        business.setLastWeekOrderQuantity(orderQuantity + quantity);
                    }
                }

                if(DatesUtil.isThismonth(orderDate)){
                    BigDecimal payAmount = business.getThisMonthPayAmount();
                    if(payAmount == null) {
                        business.setThisMonthPayAmount(actualPay);
                    }else{
                        business.setThisMonthPayAmount(payAmount.add(actualPay));
                    }
                    Integer quantity = business.getThisMonthOrderQuantity();
                    if(quantity == null){
                        business.setThisMonthOrderQuantity(orderQuantity);
                    }else{
                        business.setThisMonthOrderQuantity(orderQuantity + quantity);
                    }
                }else if(DatesUtil.isLastmonth(orderDate)){
                    BigDecimal payAmount = business.getLastMonthPayAmount();
                    if(payAmount == null) {
                        business.setLastMonthPayAmount(actualPay);
                    }else{
                        business.setLastMonthPayAmount(payAmount.add(actualPay));
                    }
                    Integer quantity = business.getLastMonthOrderQuantity();
                    if(quantity == null){
                        business.setLastMonthOrderQuantity(orderQuantity);
                    }else{
                        business.setLastMonthOrderQuantity(orderQuantity + quantity);
                    }
                }
            }
            business.setId(order.getBusinessId());
            business.setLastOrderTime(maxOrderMillisecond == null ? new Date() : new Date(maxOrderMillisecond));
            boolean isSuccess = this.esUtil.updateSelective(INDEX, TYPE, String.valueOf(order.getBusinessId()), business);
//            DocWriteResponse docWriteResponse = this.esUtil.save(INDEX, TYPE, String.valueOf(order.getBusinessId()), business);
            if(!isSuccess){
                log.error(String.format("修改订单统计信息失败,BusID:%s", order.getBusinessId()));
            }
//            if(response.status() == RestStatus.CREATED){
//                BusinessRepository.this.addToInitAcitivties(business.getId());
//            }
        }else{
            log.error(response.toString());
        }
    }

    public void statisticalBusAsync(Order order){
        if(order != null && order.getBusinessId() != null) {
            String key = String.valueOf(order.getBusinessId());
            concurrentHashMap.put(key, order, 3000);
        }
    }


//    public void initActivities(Integer businessId) throws IOException {
//        if(businessId != null) {
//            List<Activities> activities = activitiesRepository.getBusinessActivities(businessId);
//            if(activities != null && activities.size() > 0){
//                BulkRequest request = new BulkRequest();
//                for(Activities activities1 : activities){
//                    if(activities1 == null || activities1.getTarget() == null || activities1.getTarget().size() == 0){
//                        continue;
//                    }
//                    for(ActivityTarget target : activities1.getTarget()){
//                        if(target == null){
//                            continue;
//                        }
//                        UpdateRequest updateRequest = this.buildSaveActivityTargetRequest(activities1, target);
//                        request.add(updateRequest);
//                    }
//
//                }
//                if(request.requests() != null && request.requests().size() > 0){
//                    this.restHighLevelClient.bulk(request);
//                }
//            }
//        }
//    }
//
//    private void addToInitAcitivties(Integer businessId){
//        if(businessId != null) {
//            queueRunnerActivities.put(String.valueOf(businessId), businessId, 1000);
//        }
//    }
//
//    private UpdateRequest buildSaveActivityTargetRequest(Activities activity, ActivityTarget target){
//        Map<String,Object> targetMap = new HashMap<>(BeanMap.create(target));
//        targetMap.put("type", activity.getPtype());
//        targetMap.put("goodids", activity.getGoodids());
//        targetMap.put("startTime", activity.getStartTime());
//        targetMap.put("endTime", activity.getEndTime());
//        targetMap.put("createType", activity.getCreateType());
//        if(activity.getStartTime() != null){
//            targetMap.put("startTimeMilli", activity.getStartTime().getTime());
//        }
//        if(activity.getEndTime() != null){
//            targetMap.put("endTimeMilli", activity.getEndTime().getTime());
//        }
//        Map<String, Object> bean = new HashMap<>();
//        bean.put("activities", targetMap);
//        UpdateRequest request = new UpdateRequest(INDEX, TYPE, String.valueOf(target.getBusinessId()));
//        Script script = new Script(ScriptType.STORED, null, "runfast_bus_netsed_activity_target_save", bean);
//        request.script(script);
//        return request;
//    }
//
//
//    public Future<String> saveActivityTarget(ActivityTarget target){
//        if(target != null && target.getActivityId() != null && target.getBusinessId() != null) {
//            Activities activity = activitiesRepository.getActivity(String.valueOf(target.getActivityId()));
//            if(activity != null) {
//                try {
//                    UpdateRequest request = this.buildSaveActivityTargetRequest(activity, target);
//                    UpdateResponse response = this.restHighLevelClient.update(request);
//                    if (response.status() == RestStatus.OK || response.status() == RestStatus.CREATED) {
//                        return new AsyncResult<>("SUCCESS");
//                    }
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            }
//        }
//        return new AsyncResult<>("ERROR");
//    }
//
//    public Future<String> delActivityTarget(Integer businessId, Integer targetId){
//        if(businessId != null) {
//            UpdateRequest request = new UpdateRequest(INDEX, TYPE, String.valueOf(businessId));
//            Map<String,Object> params = new HashMap<>();
//            params.put("id", targetId);
//            Script script = new Script(ScriptType.STORED, null, "runfast_bus_netsed_activity_target_del", params);
//            request.script(script);
//            try {
//                UpdateResponse response = this.restHighLevelClient.update(request);
//                if (response.status() != RestStatus.OK && response.status() != RestStatus.CREATED) {
//                    return new AsyncResult<>("ERROR");
//                }
//            } catch (IOException e) {
//                e.printStackTrace();
//                return new AsyncResult<>("ERROR");
//            }
//            return new AsyncResult<>("SUCCESS");
//        }
//        return new AsyncResult<>("ERROR");
//    }
//
//    public Future<String> delActivityTargetByActivityId(Integer activityId) throws IOException {
//        if(activityId != null) {
//            SearchRequest searchRequest = new SearchRequest(INDEX);
//            searchRequest.types(TYPE);
//            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//            BoolQueryBuilder parentBuilder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("activities.activityId", activityId));
//            NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("activities", parentBuilder, ScoreMode.None);
//            sourceBuilder.query(nestedQueryBuilder).fetchSource("id", null);
//            searchRequest.source(sourceBuilder);
//            SearchResponse response = this.restHighLevelClient.search(searchRequest);
//            BulkRequest bulkRequest = new BulkRequest();
//            if(response.status() == RestStatus.OK){
//                SearchHits hits = response.getHits();
//                if(hits != null && hits.getHits() != null){
//                    for(SearchHit hit : hits){
//                        if(hit != null){
//                            Map<String, Object> map = hit.getSourceAsMap();
//                            if(map != null){
//                                Object busId = map.get("id");
//                                if(busId != null && NumberUtils.isPositiveInteger(busId.toString())){
//                                    UpdateRequest request = new UpdateRequest(INDEX, TYPE, busId.toString());
//                                    Map<String,Object> params = new HashMap<>();
//                                    params.put("activityId", activityId);
//                                    Script script = new Script(ScriptType.STORED, null, "runfast_bus_netsed_activity_target_del_by_activityid", params);
//                                    request.script(script);
//                                    bulkRequest.add(request);
//                                }
//                            }
//                        }
//                    }
//                }
//            }
//            if(bulkRequest.requests().size() > 0){
//                BulkResponse responses = this.restHighLevelClient.bulk(bulkRequest);
//                if(responses.status() == RestStatus.OK || responses.status() ==RestStatus.CREATED){
//                    return new AsyncResult<>("SUCCESS");
//                }
//            }
//        }
//        return new AsyncResult<>("ERROR");
//    }
//
//
//    private UpdateRequest setSelfDeliverRunner(Integer businessId, DeliverCost deliverCost) {
//        Map<String, Object> beanMap = new HashMap<>();
//        beanMap.put("delivercost", JSON.parseObject(JSON.toJSONString(deliverCost)));
//        Script script = new Script(ScriptType.STORED, null, "runfast_bus_netsed_deliver_cost_target_save", beanMap);
//        UpdateRequest request = new UpdateRequest(INDEX, TYPE, String.valueOf(businessId));
//        request.script(script);
//        return request;
//    }
//
//    private Future<String> setSelfDeliver(Integer businessId, Integer agentId) throws IOException {
//        DeliverCost deliverCost = deliverCostRepository.getDefaultDeliverCost(agentId);
//        if(deliverCost != null){
//            UpdateResponse responses = this.restHighLevelClient.update(this.setSelfDeliverRunner(businessId, deliverCost));
//            if(responses.status() == RestStatus.OK || responses.status() == RestStatus.CREATED){
//                return new AsyncResult<>("SUCCESS");
//            }
//        }
//        return new AsyncResult<>("ERROR");
//    }
//
//    private void setDefaultDeliverCostRunner(Integer agentId) throws IOException {
//        DeliverCost deliverCost = deliverCostRepository.getDefaultDeliverCost(agentId);
//        if(deliverCost != null) {
//            SearchRequest searchRequest = new SearchRequest();
//            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//            sourceBuilder.query(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("agentId", agentId)).mustNot(QueryBuilders.termQuery("isDeliver", 1)));
//            sourceBuilder.fetchSource("id", null);
//            searchRequest.source(sourceBuilder).indices(INDEX).types(TYPE);
//            SearchResponse response = this.restHighLevelClient.search(searchRequest);
//            if (response.status() == RestStatus.OK) {
//                SearchHits hits = response.getHits();
//                if (hits != null && hits.getHits() != null) {
//                    BulkRequest request = new BulkRequest();
//                    for (SearchHit hit : hits) {
//                        if (hit != null) {
//                            Map<String, Object> map = hit.getSourceAsMap();
//                            if (map != null) {
//                                Object busId = map.get("id");
//                                if (busId != null && NumberUtils.isPositiveInteger(busId.toString())) {
//                                    request.add(this.setSelfDeliverRunner(Integer.valueOf(busId.toString()), deliverCost));
//                                }
//                            }
//                        }
//                    }
//                    if(request.requests() != null && request.requests().size() > 0){
//                        this.restHighLevelClient.bulk(request);
//                    }
//                }
//            }
//        }
//    }
//
//    public void setDefaultDeliverCost(Integer agentId) {
//        if(agentId != null) {
//            queueRunnerDeliverCost.put(String.valueOf(agentId), agentId, 10000);
//        }
//    }

}
